

modules = typeof(modules) == 'undefined' ? [] : modules;


launch_visual_editor = (function() {
  YUI().use('node', 'event', 'io', 'io-upload-iframe', 'json', 'cookie', 'lang', function(Y) {
    //YY = Y;
    var scope = {};
    for (var i = 0; i < modules.length; i++) {
      modules[i].apply(scope, [Y]);
    }
  });
});




modules.push(function(Y) {

  var editor = this;

  YY = Y;

  var names = {
    PageThing : 'Stránka',
    ContentContainer : 'Sloupec',
    ContentInsertionThing : 'Obsah',
    PagerThing : 'Stránkovač',
    MetaLecture : 'Lekce',
    TextThing : 'Text',
    TitleThing : 'Nadpis',
    MenuThing : 'Menu'
  }



  this.send = function(eventName, params, callback) {
      params = params ? params : {};

      var json = Y.JSON.stringify({
             name: eventName,
             params: params
          });
      Y.log('SEND: '+json);

      Y.io('/admin/ve/event', {
        data: "json="+json,
        method: "POST",
        on: {
          success: function(id, reponse) {
            Y.log('REPLY: '+reponse.responseText);
            try {
              var event = Y.JSON.parse(reponse.responseText);
            } catch (e) {
              console.warn("could not parse:", reponse.responseText)
            }            
            Y.fire(event['name'], event['params']);
            if(callback) {
              callback.call();
            }
          },
          fail: function() {
            Y.log(Y.JSON.stringify(arguments[1]), 'error');
          }
        },
        context: this
      });
  }



  var n = Y.Node.create;
  var VisualEditor = function() {
    var self = this;

    this.editThing = function(node, updatePath, original) {
      if (original) {
        this.editCss(original);
      }
      
      updatePath = typeof(updatePath) != 'undefined' ? updatePath : true;

      var id = node.get('className').match('thing-([^ $]+)')[0];

      Y.io("/admin/ve/edit?p="+id, {
        on : {
          success : function (id, o, args) {
            var data = o.responseText; // Response data.
            //console.log(data);
            self.innerContent.set('innerHTML', data);
            self.enableForm();
          },
          failure: function (id, o, args) {
            var data = o.responseText; // Response data.
            //console.log(data);
            self.innerContent.set('innerHTML', data);
          }
        }
      });

      if (updatePath) {
        this.setPathTo(node);
      }
    }


    this.submitFormOnSaveHook = function(e, form) {
        if (e.ctrlKey && e.keyCode == 83) {// ctrl + s
          var id = e.target.get('id');
          var pos = getCaret(e.target._node);
          console.log(id, pos);

          self.submitForm(form, function() {
            setCaret(document.getElementById(id), pos);
          });

          e.preventDefault();
          console.log(e);
        }
      }

    this.enableForm = function(callback) {
      this.innerContent.all('script').each(function() {
        eval(this.get('innerHTML'));
      })
      
      this.innerContent.all('form').each (function() {
        var form = this;
        this.on('keydown', function(e) {
          self.submitFormOnSaveHook(e, form);
        });
        this.on('submit', function(e) {
          e.preventDefault();
          self.submitForm(form);
        })
        if (callback) {
          callback(form);
        }
      })

    }

    var unescapeHtml = function (s) {
      var temp = document.createElement("div");
      temp.innerHTML = s;
      var result = temp.childNodes[0].nodeValue;
      temp.removeChild(temp.firstChild)
      return result;
    }

    this.submitForm = function(form, callback) {
      Y.io('/admin/ve/save', {
        method: 'POST',
        form: {
          id: form,
          upload: true
        },
        on : {
          complete : function (id, o, args) {
            //console.log(arguments);
            var data = Base64.decode(o.responseText); // Response data.

            try {
              data = Y.JSON.parse(data);
              if (data.exception) {
                self.innerContent.set('innerHTML', "<iframe id=\"brutal_error\" style=\"width:100%; height:100%\"></iframe>");
                document.getElementById('brutal_error').contentDocument.write(data.exception);
                return;
              }
            } catch (e) {
              try {
                console.warn("could not parse:", o.responseText)
                self.innerContent.set('innerHTML', o.responseText);
                return;
              } catch (e) {
                console.warn('error', e);
              }

            }
            
            self.innerContent.set('innerHTML', data['form']);
            self.editing.set('innerHTML', data['change']);
            self.enableForm(callback);
          },
          failure: function (id, o, args) {
            self.innerContent.set('innerHTML', o.responseText);
          }
        }
      });
    }


    this.setPathTo = function(target) {
      var path = [];
      var node = target;
      while (node) {
        if (node.hasClass('Thing')) {
          var title = node.get('className').split(' ')[0];
          var name = typeof(names[title]) != 'undefined' ? names[title] : title;
          var item = n('<a href="#" class="nodePathITem"><span class="name">'+name+'</span></a>');
          path.push(item);
          item.on('click', function(e, node) {
            self.setPathNode(this, node);
            e.preventDefault(); // do not put # in url
          }, item, node);

          // select the target node in path
          if (node == target) {
            self.selectPathNode(item);
          }

          /*
          item.on('mousemove', function(e, node) {
            if (this.hoveredThing) {
            this.hoveredThing.removeClass('Thing-EditingHover');
            }
            this.hoveredThing = node;
            this.hoveredThing.addClass('Thing-EditingHover');
          }, item, node);

          item.on('mouseleave', function(e, node) {
            if (this.hoveredThing) {
              this.hoveredThing.removeClass('Thing-EditingHover');
            }
            this.hoveredThing = null;
          }, item, node);
          */

         
        }
        node = node.get('parentNode');
      }

      this.nodePath.set('innerHTML', '');
      for (var i = path.length-1; i > 0; i--) {
        this.nodePath.appendChild(path[i]);
        path[i].appendChild(n('<span class="separator">&gt;</span>'));
      }
      this.nodePath.appendChild(path[0]);
    }

    this.setActive = function(thing) {
      if (this.editing) {
        this.editing.removeClass('Thing-editing');
      }
      this.editing = thing;
      if (thing) {
        thing.addClass('Thing-editing');
      }
    }

    this.setPathNode = function(node, thing) {
      this.selectPathNode(node);
      this.editThing(thing, false, thing);
      self.setActive(thing);
    }

    this.css = new editor.CssRules();
    this.css.loadHash(visual_editor_css_rules);
    //this.css.setRule('div', {color:'blue'});

    this.editCss = function(thing) {
      this.innerCssContent.set('innerHTML', '');

      //
      var groups = this.css.getGroups(thing.get('tagName').toLowerCase());
      groups.push.apply(groups, this.css.getGroups('.'+thing.get('className').split(' ')[0]));



      //console.log('.'+thing.get('className'));

      var usedGroups = {};

      for (var i = groups.length; i > 0; i--) {
        var selector = groups[i-1][1][0];
        var attrs = groups[i-1][1][1];
        usedGroups[selector] = true;

        this.addCssGroup(selector, attrs);
        //console.log(groups[i-1]);
      }


      
      var proposed = this.calculateSelectorProposals(thing);
      var parent = thing.get('parentNode');
      if (parent) {
        proposed.push.apply(proposed, this.calculateSelectorProposals(parent));
      }
      parent = parent.get('parentNode');
      if (parent) {
        proposed.push.apply(proposed, this.calculateSelectorProposals(parent));
      }
      parent = parent.ancestor('.Thing');
      if (parent) {
        proposed.push.apply(proposed, this.calculateSelectorProposals(parent));
      }

      var proposalsWithPriorities = [];

      for (i = proposed.length; i > 0; i--) {
        proposalsWithPriorities.push(
          [proposed[i-1].split(' ').length*10 + proposed[i-1].length, proposed[i-1]
        ]);
      }

      proposalsWithPriorities.sort().reverse();

      proposed = [];
      for (i = proposalsWithPriorities.length; i > 0; i--) {
        proposed.push(proposalsWithPriorities[i-1][1]);
      }

      for (i = proposed.length; i > 0; i--) {
        selector = proposed[i-1];
        if (!usedGroups[selector]) {
          groups = this.css.getGroups(selector);
          for (var g = groups.length; g > 0; g--) {
            var sel = groups[g-1][1][0];
            if (!usedGroups[sel]) {
              usedGroups[sel] = true;
              this.addCssGroup(sel, groups[g-1][1][1]);
            }
          }
          if (!usedGroups[selector]) {
            this.addCssGroup(selector, {});
          }
        }
      }
    }

    this.addCssGroup = function(selector, attrs) {
      var groupEl, groupInner;
      groupEl = this.innerCssContent.appendChild(n('<div class="cssGroup"></div>'));
      groupEl.appendChild(n('<span class="cssGroupTitle"><span class="selector">'+selector+'</span> <span class="brace">{</span></span>'));
      groupInner = groupEl.appendChild(n('<div class="cssGroupInner"></div>'));
      groupEl.appendChild(n('<span class="cssGroupEnd"><span class="brace">}</span></span>'));
      for (var attr in attrs) {
        if (Y.Lang.isString(attrs[attr])) {
          this.addCssRule(groupInner, attr, attrs[attr]);
        }
      }
    }

    this.calculateSelectorProposals = function(thing) {
      var proposed = [];
      if (thing.hasClass('Thing')) {
        proposed.push('.'+thing.get('className').split(' ')[0]);
      } else {
        proposed.push(thing.get('tagName').toLowerCase());
        var class_name = thing.get('className').split(' ')[0];
        if (class_name != 'Thing') {
          proposed.push('.'+class_name);
        }
      }

      var proposedRoots = proposed.slice();

      //var maxLen = 5;

      for (var i = 0; i < proposedRoots.length; i++) {
        var parentThing = thing.ancestor('.Thing');
        if (parentThing) {
          var parentThingClass = '.'+parentThing.get('className').split(' ')[0];
          proposed.push(parentThingClass+' '+proposedRoots[i]);

          var ppThing = parentThing.ancestor('.Thing');

          if (ppThing) {
            var parentParentThingClass = '.'+ppThing.get('className').split(' ')[0];
            proposed.push(parentParentThingClass+' '+parentThingClass+' '+proposedRoots[i]);
          }
        }
      }
      return proposed;
    }

    this.addCssRule = function(inner, attr, value) {
      return inner.appendChild(
        n('<div class="cssPair"><span class="static"><span class="cssAttr">'+attr+'</span>: <span class="cssValue">'+value+'</span>;</span></div>')
      );
    }

    this.selectPathNode = function(node) {
      try {
        if (this.pathNode) {
          this.pathNode.removeClass('nodePathITem-selected');
        }
      } catch (e) {}
      this.pathNode = node;
      this.pathNode.addClass('nodePathITem-selected');
    }


    this.setHeight = function(top, height) {
      self.node.set('style.height', height+'px');
      page.set('style.height', top+'px');
      self.inner.set('style.height', (height-20)+'px');

      Y.Cookie.set("editorHeight", height);
    }



    this.node = n('<div class="visual_editor"></div>');


    // toolbar -----------------------------------------------------------------

    this.toolbar = n('<div class="toolbar"><span class="toolbarTitle">cesta:</span></div>');
    this.node.appendChild(this.toolbar);

    this.nodePath = n('<span class="nodePath"></span>');
    this.toolbar.appendChild(this.nodePath);



    this.inner = n('<div class="inner"></div>');
    this.node.appendChild(this.inner);

    this.innerContent = n('<div class="innerContent"></div>');
    this.inner.appendChild(this.innerContent);

    this.innerCss = n('<div class="innerCss"></div>');
    this.inner.appendChild(this.innerCss);

    this.innerCssContent = n('<div class="content"></div>');
    this.innerCss.appendChild(this.innerCssContent);



    this.innerCssContent.on('click', function(e) {
      var pair;
      if (e.target.test('.cssGroupTitle, .cssGroupTitle *')) { //e.target.get('className') == 'cssPair') {
        var inner = e.target.ancestor('.cssGroup').one('.cssGroupInner');
        pair = self.addCssRule(inner, "", "");
      } else {
        pair = e.target.ancestor('.cssPair');
        if (pair.get('className') != 'cssPair') {
          return;
        }
      }
      self.editPair(pair);
    });


    this.editPair = function(pair) {
      var attrEl = pair.get('firstChild.firstChild');
      var valueEl = pair.get('firstChild.children.1');
      pair.addClass('editing');

      //PAIR = pair;

      var editorEl = n('<div class="editor"></div>');
      var input = editorEl.appendChild(n('<input type="text" />'));

      var attr = attrEl.get('innerText');
      if (attr != '') {
        input.set('value',
          attr +': '+ valueEl.get('innerText').trim()
        );
      }

      pair.appendChild(editorEl);

      input.focus();

      input.on('blur', function() {
        self.submitFinishCssEdit(pair, input);
      })

      input.on('keyup', function(e) {
        console.log(arguments);
        if (e.button == 27) {
          e.target.blur();
        }
        if (e.button == 13) {
          //self.submitFinishCssEdit(pair, input);
          var inner = input.ancestor('.cssGroupInner');
          e.target.blur();

          var pair = self.addCssRule(inner, "", "");
          self.editPair(pair);
        }
      })
    }


    this.submitFinishCssEdit = function(pair, input) {
      pair.removeClass('editing');
      var selector = pair.
        get('parentNode.parentNode.firstChild.firstChild.innerHTML');
      var attrEl = pair.get('firstChild.firstChild');
      var valueEl = pair.get('firstChild.children.1');

      var rule = input.get('value');

      var sheets = editor.editingDocument._node.styleSheets;

      var mysheet = sheets[sheets.length-1];
      var totalrules = 0;
      try {
        totalrules = mysheet.cssRules ? mysheet.cssRules.length : mysheet.rules.length;
      } catch (e) {}

      if (mysheet.deleteRule){ //if Firefox
        //mysheet.deleteRule(totalrules-1)
        mysheet.insertRule(selector+"{"+rule+"}", totalrules);
      } else if (mysheet.removeRule){ //else if IE
        //mysheet.removeRule(totalrules-1)
        mysheet.addRule(selector, rule);
      }

      r = rule.split(':');
      r[0] = self.css.normalizeSelector(r[0]);

      editor.send('css_change', {
        selector: selector,
        attribute: r[0],
        value: r[1]
      });

      console.log(rule, r);

      if (rule == '' || r[0].match(/^\s+$/) || r.length < 2 || r[1].match(/^\s+$/)) {
        input.ancestor('.cssPair').remove();
      } else {
        attrEl.set('innerHTML', r[0]);
        valueEl.set('innerHTML', r[1]);
        input.get('parentNode').remove();
        var o = {};
        o[r[0]] = r[1];
        console.log(selector, o);
        self.css.setRule(selector, o);
      }
    }


    Y.get('body').appendChild(this.node);


    var page = Y.get('#page');
    var dragging = false;
    var docHeight = docHeight = Y.get('body').get('docHeight');
    var clickOffset = null;

    this.toolbar.on('mousedown', function (e) {
      docHeight = Y.get('body').get('docHeight');
      clickOffset = (self.node.getY() - e.pageY);
      console.log('height', self.node.getY());

      dragging = true;
      console.log(clickOffset, docHeight, dragging);
      e.preventDefault();
    });


    this.documentMouseUp = function (e) {
      dragging = false;
    }


    this.documentMouseMove = function (e) {
      if (dragging) {
        self.setHeight(e.pageY + clickOffset, docHeight - e.pageY - clickOffset);
        e.preventDefault();
      }
//      else {
//        if (this.hoveredThing) {
//        this.hoveredThing.removeClass('Thing-EditingHover');
//        }
//        this.hoveredThing = e.target;
//        this.hoveredThing.addClass('Thing-EditingHover');
//      }
    }

    Y.get('body').on('mousemove', this.documentMouseMove);

    var storedHeight = Y.Cookie.get("editorHeight") || 300;
    this.setHeight(docHeight - storedHeight, storedHeight);


    Y.get('body').on('mouseup', this.documentMouseUp);


    visual_editor_inner_loaded = function() {
      editor.editingDocument = Y.get('#page').get('contentDocument');
      var body = editor.editingDocument;//.get('body');
      
      body.on('mousemove', self.documentMouseMove);
      body.on('mouseup', self.documentMouseUp);
      body.on('click', function (e) {
        var original = Y.get(e.target);
        var node = original;

        var found = false;

        while (node) {
          if (node.hasClass('Thing')) {
            if (node != this.editing) {
              self.editThing(node, true, original);
              self.setActive(node);
            } else {
              self.setActive(null);
            }
            found = true;
            break;
          }
          node = node.get('parentNode');
        }

        if (!found) {
          self.editCss(Y.Node.get('body'));
          self.setActive(null);
          //console.log('aaaaaaaaa', Y.Node.get('body'));
        }
      });
    }

    if (visual_editor_inner_loaded_called) {
      visual_editor_inner_loaded();
    }

  }


  visual_editor_instance = new VisualEditor();





  function getCaret(el)
  {
      var pos = 0;
      // IE Support
      if (document.selection)
      {
          el.focus ();
          var Sel = document.selection.createRange();
          var SelLength = document.selection.createRange().text.length;
          Sel.moveStart ('character', -el.value.length);
          pos = Sel.text.length - SelLength;
      }
      // Firefox support
      else if (el.selectionStart || el.selectionStart == '0')
          pos = el.selectionStart;

      return pos;
  }


  function setCaret(obj, pos) {
    //FOR IE
    if(obj.setSelectionRange) {
      obj.focus();
      obj.setSelectionRange(pos,pos);
    }

    // For Firefox
    else if (obj.createTextRange) {
      var range = obj.createTextRange();
      range.collapse(true);
      range.moveEnd('character', pos);
      range.moveStart('character', pos);
      range.select();
    }
  }

});

